using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;

namespace Charisma
{
    [DebuggerDisplay("{MappingName}")]
    public class EntityConfiguration
    {
        private Type type;

        public EntityConfiguration(Type type)
        {
            Contract.Requires(type != null);

            Type = type;
            Members = new EntityMemberConfigurationSet();
        }

        public EntityConfiguration(string name)
        {
            Contract.Requires(!string.IsNullOrEmpty(name));

            Name = name;
            Members = new EntityMemberConfigurationSet();
        }

        public string Name { get; set; }

        public EntityMemberConfigurationSet Members { get; private set; }

        public Type Type
        {
            get { return type; }
            private set
            {
                type = value;

                if (type == null)
                    return;

                Name = type.Name;
            }
        }

        #region Property building members

        internal EntityMemberConfiguration RegisterMember(MemberInfo field)
        {
            Contract.Requires(field != null);
            Contract.Requires(field is PropertyInfo || field is FieldInfo);

            EntityMemberConfiguration result = Members.Where(p => p.Field == field).FirstOrDefault();
            if (result == null)
            {
                result = BuildMember(field);
                Members.Add(result);
            }
            return result;
        }

        private static EntityMemberConfiguration BuildMember(MemberInfo field)
        {
            Contract.Requires(field != null);
            Contract.Requires(field is PropertyInfo || field is FieldInfo);

            return new EntityMemberConfiguration(field);
        }

        internal EntityMemberConfiguration RegisterKeyMember(MemberInfo field)
        {
            Contract.Requires(field != null);
            Contract.Requires(field is PropertyInfo || field is FieldInfo);

            // Identity property must be unique so removing previous IsKey attribute if any
            EntityMemberConfiguration currentIdentity = Members.Where(p => p.IsKey).FirstOrDefault();
            if (currentIdentity != null)
            {
                // The same member as it was, so doing nothing
                if (currentIdentity.Field == field)
                    return currentIdentity;
                else
                {
                    currentIdentity.IsKey = false;
                }
            }

            EntityMemberConfiguration configuration = RegisterMember(field);

            configuration.IsIgnored = false;
            configuration.IsKey = true;

            return configuration;
        }

        #endregion

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(Type != null);
            Contract.Invariant(Members != null);
        }
    }
}